package com.wsoft.system.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wsoft.config.CustomSchedulerListener;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.entity.TsJobEntity;
import com.wsoft.enums.JobStatusEnum;
import com.wsoft.system.manager.TsJobManager;
import com.wsoft.system.mapper.TsJobMapper;
import com.wsoft.query.job.*;
import com.wsoft.system.service.ITsJobService;
import com.wsoft.utils.JobUtil;
import com.wsoft.vo.JobDetailVO;
import com.wsoft.vo.JobVO;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * <p>
 * 定时任务表 服务实现类
 * </p>
 *
 * @author z001
 * @since 2024年10月23日 08:16:34
 */
@Slf4j
@Service
public class TsJobServiceImpl extends ServiceImpl<TsJobMapper, TsJobEntity> implements ITsJobService {

    @Resource
    private TsJobManager jobManager;
    @Resource
    private Scheduler scheduler;

    private static final String DEFAULT_JOB_GROUP = "default_job_group";

    private static final String DEFAULT_TRIGGER_GROUP = "default_trigger_group";

    private static final String TRIGGER_PRE = "Trigger_";
    /**
     * 默认返回执行次数
     */
    private final Integer cornCount = 5;

    /**
     * 新增任务
     *
     * @param query
     * @return
     */
    @Override
    public boolean insertJob(JobInsertQuery query) {
        //校验code
        validateJobCode(query);
        //校验corn
        JobUtil.checkCron(query.getCron());
        //保存
        TsJobEntity entity = Convert.convert(TsJobEntity.class, query);
        entity.setStatus(JobStatusEnum.PAUSE.getType());
        return jobManager.save(entity);
    }

    /**
     * 修改任务
     *
     * @param query
     * @return
     */
    @Override
    public boolean updateJob(JobUpdateQuery query) {
        TsJobEntity jobEntity = jobManager.getById(query.getId());
        //校验任务状态
        validateJobStatus(jobEntity.getStatus());
        //校验code
        JobUtil.checkCron(query.getCron());
        //校验corn
        JobUtil.checkCron(query.getCron());

        TsJobEntity entity = Convert.convert(TsJobEntity.class, query);
        entity.setStatus(JobStatusEnum.PAUSE.getType());
        return jobManager.updateById(entity);
    }

    /**
     * 查询任务列表
     *
     * @param query
     * @return
     */
    @Override
    public PageInfo listJob(JobListQuery query) {
        PageHelper.startPage(query.getPageNum(), query.getPageSize());
        List<TsJobEntity> entityList = jobManager.listJob(query);
        PageInfo pageInfo = PageInfo.of(entityList);
        List<TsJobEntity> list = pageInfo.getList();

        List<JobVO> result = new ArrayList<>();
        for (TsJobEntity entity : list) {
            JobVO vo = Convert.convert(JobVO.class, entity);
            if (JobStatusEnum.START.getType().equals(vo.getStatus())) {
                //赋值后几次运行时间
                List<LocalDateTime> nextTimeList = JobUtil.queryNextTimeFromNow(entity.getCron(), cornCount);
                vo.setNextJobExecuteTimeList(nextTimeList);
            }
            result.add(vo);
        }
        pageInfo.setList(result);
        return pageInfo;
    }

    /**
     * 获取任务详情
     *
     * @param id
     * @return
     */
    @Override
    public JobVO getJobInfo(Long id) {
        TsJobEntity entity = jobManager.getById(id);
        if (null == entity) {
            return new JobVO();
        }
        JobVO vo = Convert.convert(JobVO.class, entity);
        // 计算未来5次执行时间
        if (JobStatusEnum.START.getType().equals(entity.getStatus())) {
            List<LocalDateTime> nextTimeList = JobUtil.queryNextTimeFromNow(entity.getCron(), cornCount);
            vo.setNextJobExecuteTimeList(nextTimeList);
        }
        return vo;
    }

    /**
     * 删除任务
     *
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteJob(Long id) throws Exception {
        TsJobEntity jobEntity = jobManager.getById(id);
        //校验任务状态
        validateJobStatus(jobEntity.getStatus());
        boolean b = jobManager.removeById(id);
        if (b) {
            deleteCronJob(jobEntity.getCode());
        }
        return b;
    }

    /**
     * 修改任务状态
     *
     * @param query
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(JobUpdateQuery query) throws Exception {
        TsJobEntity jobEntity = jobManager.getById(query.getId());
        if (jobEntity.getStatus().equals(query.getStatus())) {
            return true;
        }
        jobEntity.setStatus(query.getStatus());
        boolean b = jobManager.updateById(jobEntity);
        if (b) {
            if (JobStatusEnum.PAUSE.getType().equals(jobEntity.getStatus())) {
                //修改为暂停,删除任务
                deleteCronJob(jobEntity.getCode());
            } else if (JobStatusEnum.START.getType().equals(jobEntity.getStatus())) {
                //启动任务
                startCronJob(jobEntity);
            }
        }
        return b;
    }

    /**
     * 执行一次
     *
     * @param query
     */
    @Override
    public boolean executeJobOne(JobExecutorQuery query) throws Exception {
        TsJobEntity jobEntity = jobManager.getById(query.getId());
        JobKey jobKey = JobKey.jobKey(jobEntity.getCode(), DEFAULT_JOB_GROUP);
        JobDetail job = JobBuilder.newJob(getClass(jobEntity.getClassPath()).getClass())
                .withIdentity(jobKey).build();

        job.getJobDataMap().put(CommonConstant.JOB_PARAMS, StrUtil.isNotBlank(query.getParams()) ? query.getParams() : jobEntity.getParams());

        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobEntity.getCode(), DEFAULT_TRIGGER_GROUP))
                .build();

        // 启动调度器
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
        return true;
    }

    @Override
    public List<LocalDateTime> nextExecute(JobNextExecutorQuery query) {
        //验证表达式
        JobUtil.checkCron(query.getCron());
        if (query.getCount() == 0) {
            query.setCount(cornCount);
        }
        return JobUtil.queryNextTimeFromNow(query.getCron(), query.getCount());
    }

    @Override
    public List<JobDetailVO> getExecutingJobs() throws SchedulerException {

        // 获取所有触发器的键
        Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(GroupMatcher.anyGroup());

        List<JobDetailVO> list = new ArrayList<>();
        for (TriggerKey triggerKey : triggerKeys) {
            // 获取触发器状态
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);

            // 过滤状态为 NORMAL 的触发器 ==> 启动的状态
            if (triggerState == Trigger.TriggerState.NORMAL) {
                Trigger trigger = scheduler.getTrigger(triggerKey);
                JobKey jobKey = trigger.getJobKey();
                JobDetail jobDetail = scheduler.getJobDetail(jobKey);

                JobDetailVO detailVO = new JobDetailVO();
                detailVO.setTriggerCode(triggerKey.getName());
                detailVO.setTriggerGroup(triggerKey.getGroup());
                detailVO.setJobCode(jobKey.getName());
                detailVO.setJobGroup(jobKey.getGroup());
                detailVO.setJobClass(jobDetail.getJobClass().getName());
                detailVO.setJobDataMap(jobDetail.getJobDataMap());
                list.add(detailVO);
            }
        }
        return list;
    }

    //############################### 私有函数 ###############################

    /**
     * 校验 Code 是否存在
     */
    private <T extends JobBaseQuery> void validateJobCode(T query) {
        TsJobEntity entity = jobManager.getByCode(query.getCode());
        if (query instanceof JobInsertQuery && null != entity) {
            //页面编码已存在
            Asserts.fail("[ts.code.exist]");
        }
        if (query instanceof JobUpdateQuery &&
                null != entity &&
                !((JobUpdateQuery) query).getId().equals(entity.getId())) {
            //页面编码已存在
            Asserts.fail("[ts.code.exist]");
        }
    }

    /**
     * 校验 任务 状态
     *
     * @param status
     */
    private void validateJobStatus(Integer status) {
        if (JobStatusEnum.START.getType().equals(status)) {
            //暂停状态下才能操作
            Asserts.fail("[job.pause.continue]");
        }
    }


    /**
     * 启动任务
     *
     * @param jobEntity
     */
    public void startCronJob(TsJobEntity jobEntity) throws Exception {
        // 当前任务不存在才进行添加
        JobKey jobKey = JobKey.jobKey(jobEntity.getCode(), DEFAULT_JOB_GROUP);
        if (scheduler.checkExists(jobKey)) {
            //编码已存在
            log.warn("[定时任务启动]失败,定时编码已存在:{}", jobEntity.getCode());
            return;
        }

        // 构建 Job
        JobDetail job = JobBuilder.newJob(getClass(jobEntity.getClassPath()).getClass())
                .withIdentity(jobKey).build();
        job.getJobDataMap().put(CommonConstant.JOB_PARAMS, jobEntity.getParams());
        // cron表达式定时构造器
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCron());

        // 构建 Trigger
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(TriggerKey.triggerKey(TRIGGER_PRE + jobEntity.getCode(), DEFAULT_TRIGGER_GROUP))
                .withSchedule(cronScheduleBuilder).build();
        // 启动调度器
        scheduler.scheduleJob(job, trigger);
        scheduler.start();


        // 注册 SchedulerListener
        CustomSchedulerListener schedulerListener = new CustomSchedulerListener();
        scheduler.getListenerManager().addSchedulerListener(schedulerListener);

        log.info("[定时任务启动],启动时间:{},启动code:{}", DateUtil.date(), jobEntity.getCode());
    }

    /**
     * 删除任务
     *
     * @param jobCode
     */
    public void deleteCronJob(String jobCode) throws Exception {
        JobKey jobKey = JobKey.jobKey(jobCode, DEFAULT_JOB_GROUP);
        TriggerKey triggerKey = TriggerKey.triggerKey(TRIGGER_PRE + jobCode, DEFAULT_TRIGGER_GROUP);
        Trigger trigger = scheduler.getTrigger(triggerKey);
        if (null == trigger) {
            return;
        }
        //暂停触发器
        scheduler.pauseTrigger(triggerKey);
        // 移除触发器
        scheduler.unscheduleJob(triggerKey);
        // 删除任务
        scheduler.deleteJob(jobKey);
        log.info("[定时任务关闭],关闭时间:{},关闭code:{}", DateUtil.date(), jobCode);

    }

    /**
     * 获取类名
     *
     * @param className
     * @return
     * @throws Exception
     */
    public static Job getClass(String className) throws Exception {
        Class<?> classTemp = Class.forName(className);
        return (Job) classTemp.newInstance();
    }

}
